package edu.ntu.cloudroid.xacml.builder.request;

import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.appengine.api.datastore.KeyFactory;
import com.sun.xacml.Indenter;
import com.sun.xacml.attr.DoubleAttribute;
import com.sun.xacml.attr.StringAttribute;
import com.sun.xacml.ctx.Attribute;
import com.sun.xacml.ctx.RequestCtx;
import com.sun.xacml.ctx.Subject;

import edu.ntu.cloudroid.model.profile.ProfileData;
import edu.ntu.cloudroid.xacml.Utilities;
import edu.ntu.cloudroid.xacml.XACMLException;
import edu.ntu.cloudroid.xacml.XACMLStrings;

public class RequestBuilder
{
   private static final Logger logger = Logger.getLogger(RequestBuilder.class
         .getName());

   protected ProfileData subjectPerson;
   protected ProfileData resourcePerson;
   List<Attribute> addAttrs; 
   

   /**
    * building the request, with extra attributes 
    */
   public RequestBuilder(ProfileData subject, ProfileData resource, List<Attribute> inputAtts){
	   subjectPerson = subject;
	   resourcePerson = resource;
	   this.addAttrs = inputAtts;	   
   }
   
   /**
    * Sets up the Subject section of the request. This Request only has one
    * Subject section, and it uses the default category. To create a Subject
    * with a different category, you simply specify the category when you
    * construct the Subject object.
    * 
    * @return a Set of Subject instances for inclusion in a Request
    * 
    * @throws URISyntaxException
    *            if there is a problem with a URI
    */
   public Set<Subject> setupSubjects() throws URISyntaxException
   {
      HashSet<Attribute> attributes = new HashSet<Attribute>();

      // setup the id and value for the requesting subject
      URI subjectId = new URI(XACMLStrings.SUBJECT_ID);
      StringAttribute value = new StringAttribute(
            KeyFactory.keyToString(subjectPerson.getKey()));

      // add the subject's identity
      attributes.add(new Attribute(subjectId, null, null, value));

      for (Attribute att : this.addAttrs)
    	  attributes.add(att);
    		   
      // bundle the attributes in a Subject with the default category
      HashSet<Subject> subjects = new HashSet<Subject>();
      subjects.add(new Subject(new URI(XACMLStrings.CATEGORY_ACCESS_SUBJECT),
            attributes));

      return subjects;
   }

   /**
    * Creates a Resource specifying the resource-id, a required attribute.
    * 
    * @return a Set of Attributes for inclusion in a Request
    * 
    * @throws URISyntaxException
    *            if there is a problem with a URI
    */
   public Set<Attribute> setupResource() throws URISyntaxException
   {
      HashSet<Attribute> resource = new HashSet<Attribute>();

      // the resource being requested
      StringAttribute value = new StringAttribute(
            KeyFactory.keyToString(resourcePerson.getKey()));
      resource.add(new Attribute(new URI(XACMLStrings.RESOURCE_ID), null, null,
            value));

      return resource;
   }

   /**
    * Creates an Action specifying the action-id, an optional attribute.
    * 
    * @return a Set of Attributes for inclusion in a Request
    * 
    * @throws URISyntaxException
    *            if there is a problem with a URI
    */
   public Set<Attribute> setupAction() throws URISyntaxException
   {
      HashSet<Attribute> action = new HashSet<Attribute>();

      URI actionId = new URI(XACMLStrings.ACTION_ID);
      action.add(new Attribute(actionId, null, null,
            new StringAttribute("read")));

      return action;
   }

   public RequestBuilder()
   {
	   
   }

   public RequestCtx getRequest() throws XACMLException
   {
      RequestCtx request;
      try {
         request = new RequestCtx(setupSubjects(), setupResource(),
               setupAction(), new HashSet<Attribute>());             
         return request;
      } catch (URISyntaxException e) {
         throw new XACMLException(e);
      }
   }

   @Override
   public String toString()
   {
      Utilities.setUpFactory();

      ByteArrayOutputStream os = new ByteArrayOutputStream();

      try {
         getRequest().encode(os, new Indenter());
         return os.toString("UTF-8");
      } catch (Exception e) {
         logger.log(Level.WARNING,
               "Error converting policy to string representation. Exception: "
                     + e.getMessage());
      }

      return null;
   }
}
